﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Secams.model;
using System.Threading;
using LumenWorks.Framework.IO.Csv;
using System.IO;

namespace Secams.controller
{
    public interface EventListener
    {
        void onNewEvent(NiEvent niEvent);
        
        // A new trial begun
        void onNewTrial(int trialNum, string scenarioName, int mode);

        // A new trial begun
        void onEndTrial(int trialNum, string scenarioName);

        // All simulation finished I.e. last trial
        void onFinished();

    }

    /// <summary>
    /// Simulates the event monitoring module
    /// In our case, it reads from CSV file and 
    /// 
    /// </summary>
    public class EventMonitorSimulator
    {
        protected List<EventListener> _eventListeners = new List<EventListener>();

        ScenarioInstanceSequence _scenarioSeq = null;
        //Scenario _currentScenario = null;
        ScenarioInstance _currentScenarioInstance = null;

        protected bool _infiniteLoop = false; // true = repeats infinetely
        protected bool _done = false; // end the (infinite) loop
        protected bool _breakScenarioFlag = false; // break the scenario (i.e. stop and move on to the next scenario)

        protected int _delayBetweenEvents = 2;

        protected int _trialCount = 0;  // Internatl trial count

        protected static bool _isPaused = false;
        static AutoResetEvent _autoEvent = new AutoResetEvent(false);

        NiEvent _currentEvent = null;


        public EventMonitorSimulator()
        {
        }

        public void registerListener(EventListener listener)
        {
            _eventListeners.Add(listener);
        }

        public ScenarioInstance getCurrentScenarioInstance()
        {
            // _currentScenarioInstance = _scenarioSeq.getScenario(_trialCount % _scenarioSeq.getSize())
            return _currentScenarioInstance;
        }

        public Scenario getCurrentScenario()
        {
            return _currentScenarioInstance.scenario;
        }

        public int getCurrentScenarioMode()
        {
            return _scenarioSeq.getScenarioMode(_trialCount % _scenarioSeq.getSize());
        }

        public int resetTrialCount()
        {
            return _trialCount = 0;
        }
        public int getTrialCount()
        {
            return _trialCount;
        }
        public void incrementTrialCount()
        {
            lock (this)
                _trialCount++;
        }

        public void breakScenario()
        {
            this._breakScenarioFlag = true;
        }

        public static void pauseThread()
        {
            _isPaused = true;
        }
        public static void resumeThread()
        {
            if (_isPaused){
                _isPaused = false;
                _autoEvent.Set();
            }  
        }

        public NiEvent getCurrentEvent()
        {
            return _currentEvent;
        }

        public void stopThread()
        {
            resumeThread();
            _done = true;
        }

        /// <summary>
        /// Creates a new thread
        /// </summary>
        public void startThread()
        {
            if (_scenarioSeq.getSize() == 0)
                throw new Exception("No Scenario was loaded");
            Thread monitorThread = new Thread(new ThreadStart(this.run));

            monitorThread.Start();
            _done = false;
        }

        /// <summary>
        /// Run the thread
        /// </summary>
        public void run()
        {
            
            while (!_done)
            {
                // Break if done
                if (_done)
                    break;

                _currentScenarioInstance = _scenarioSeq.getScenarioInstance(_trialCount % _scenarioSeq.getSize());
                notifyNewTrial(_trialCount, _currentScenarioInstance.scenario.name, this.getCurrentScenarioMode());
                foreach (NiEvent anEvent in _currentScenarioInstance.scenario.events)
                {
                    if (_done)
                        break;
                    if (_breakScenarioFlag)
                    {
                        _breakScenarioFlag = false;
                        break;
                    }

                    anEvent.timestamp = DateTime.Now;
                    _currentEvent = anEvent;
                    notifyListeners(anEvent);
                    System.Console.WriteLine("TEST:"+anEvent.signature);
                    Thread.Sleep(_currentScenarioInstance.delayBetweenEvents*1000);

                    while (_isPaused) {
                        _autoEvent.WaitOne();
                    }
                }
                // Clear breakFlag at the last event (or otherwise it will be applied in the next scenario)
                _breakScenarioFlag = false;
                notifyEndTrial(_trialCount, _currentScenarioInstance.scenario.name);
                incrementTrialCount();

                // Also break if infiniteLoop is false and trialCount reached the number of scenarios
                if (!_infiniteLoop && _trialCount == _scenarioSeq.getSize())
                    break;

            }
            notifyFinished();
            
        }

        public void setScenarioSeq(ScenarioInstanceSequence scenarioSeq)
        {
            _scenarioSeq = scenarioSeq;
        }
        public ScenarioInstanceSequence scenarioSeq
        {
            get {return _scenarioSeq; }
            set { _scenarioSeq = value; }
        }

        protected void notifyListeners(NiEvent niEvent)
        {
            foreach (EventListener listener in _eventListeners)
            {
                listener.onNewEvent(niEvent);
            }
        }

        protected void notifyNewTrial(int trialNum, string scenarioName, int mode)
        {
            foreach (EventListener listener in _eventListeners)
            {
                listener.onNewTrial(trialNum, scenarioName, mode);
            }
        }

        protected void notifyEndTrial(int trialNum, string scenarioName)
        {
            foreach (EventListener listener in _eventListeners)
            {
                listener.onEndTrial(trialNum, scenarioName);
            }
        }

        protected void notifyFinished()
        {
            foreach (EventListener listener in _eventListeners)
            {
                listener.onFinished();
            }
        }

        
    }
}
